<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/jsp/commons/include.jsp"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<title>JavaScript Demo</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<link />
	<style type="text/css">
		#container{
		    width: 760px;
		    height: 600px;
		    margin: 30px auto;
		    /* IE fix 居中布局 */
		    text-align: center;
		}
		 
		#main{
		    width: 760px;
		    height: 600px;
		    margin: 30px auto;
		    /* IE fix 居中布局 */
		 	text-align: left;
		}
	</style>
	
	<script type="text/javascript">
		/*
			理解Javascript的闭包
			http://coolshell.cn/articles/6731.html
		*/
		
		var newLine = "<br/>";	

		/* 在ECMAScript中，函数对象中定义的内部函数(inner function)是可以直接访问外部函数的局部变量 */
		function greeting(name) {
		    var text = 'Hello ' + name; // local variable
		    // 每次调用时，产生闭包，并返回内部函数对象给调用者
		    return function() { document.write(text); };
		}
		var sayHello=greeting("Closure");
		sayHello();  // 通过闭包访问到了局部变量text
		
		document.write("<hr/>");
		
		/* ECMAScript闭包模型 */
		
		/* 闭包的样列 */
		
		// 例子1:闭包中局部变量是引用而非拷贝  因此执行结果应该弹出的667而非666。
		document.write("例子1:闭包中局部变量是引用而非拷贝  因此执行结果应该弹出的667而非666。<br/>");
		function say667() {
			// Local variable that ends up within closure
			var num = 666;
			var sayAlert = function() {document.write(num);};
			num++;
			return sayAlert;
		}
		var sayAlert = say667();
		sayAlert();
		document.write("<hr/>");
		
		// 例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。
		document.write("例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。<br/>");
		function setupSomeGlobals() {
			// Local variable that ends up within closure
			var num = 666;
			 // Store some references to functions as global variables
			gAlertNumber = function() {document.write(num+"<br/>");};
			gInscreaseNumber = function() {num++;};
			gSetNumber = function(x) {num = x;};
		}
		setupSomeGlobals();// 为三个全局变量赋值
		gAlertNumber();
		gInscreaseNumber();
		gAlertNumber();
		gSetNumber(12);
		gAlertNumber();
		document.write("<hr/>");
		
		// 例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包
		document.write("例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包 <br/>");
		function buildList(list) {
			var result = [];
			for (var i=0; i < list.length; i++) {
				var item = 'item' + list[i];
				result.push(function(){document.write(item + ' ' + list[i] + "<br/>");});
			}
			return result;
		}
		
		function testList() {
			var fnList = buildList([1, 2, 3]);
			// using j only to help prevent confusion - could use i
			for (var j=0; j < fnList.length; j++) {
				fnList[j]();
			}
		};
		testList();
		/* 
		testList的执行结果是弹出item3 undefined窗口三次，因为这三个函数绑定了同一个闭包，
		而且item的值为最后计算的结果，但是当i跳出循环时i值为4，所以list[4]的结果为undefined. 
		*/
		
		// 例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。
		document.write("<hr/>例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。<br/>");
		function sayAlice() {
			var sayWrite = function(){document.write(alice);};
			var alice = "Hello Alice";
			return sayWrite;
		}
		
		var helloAlice = sayAlice();
		helloAlice();
		/*
		执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后，
		局部变量仍然可以被访问到。
		*/
		// 例子5：每次函数调用的时候创建一个新的闭包
		document.write("<hr/>例子5：每次函数调用的时候创建一个新的闭包<br/>");
		function newClosure(someNum, someRef) {
			var num = someNum;
			var anArray = [1, 2, 3];
			var ref = someRef;
			return function(x) {
				num += x;
				anArray.push(num);
				document.write("num: " + num 
						+ " anArray: " + anArray.toString() 
						+ " someVar: " + ref.someVar + "<br/>");
			}
		}
		
		closure1 = newClosure(5, {someVar: 'closure1'});
		closure2 = newClosure(1000, {someVar: 'closure2'});
		closure1(40);
		closure2(-10);
		
		/* 闭包的应用 */
		document.write("<hr/>Singleton 单件<br/>");
		var singleton = function() {
			var privateVar = "这是一个私有变量privateVar";
			function privateFun() {
				document.writeln("privateFun->" + privateVar + newLine);
			};
			return {
				firstMethod: function(a, b) {
					document.writeln("args: a=" + a + " b=" + b 
							+ " firstMethod-->" + privateVar + newLine);
				},
				secondMethod: function(c) {
					document.writeln("args: c=" + c + " secondMethod" + newLine);
					privateFun();
				}
			};
		}();
		singleton['firstMethod'](1, 2);
		singleton['secondMethod'](3);
		
	</script>
</head>

<body>
	<div id="container">
		<div id="main">
				
		</div>
	</div>
</body>
</html>
